舊觀念組成新結構,這些結構形成新觀念,新觀念不斷復合,並無休止地持續下去,越來越遠離每個人的最原始的本質。這就是人類思維的運作方式。
Douglas R. Hofstadter
說到class。大概第一個念頭就是物件導向程式設計。畢竟,OOP的根源即源於class的概念。class是創建實例instance的藍圖。大體來說,它們封裝了數據與程式碼以操作改變(mutate)數據。
在JS中,class建立於原型prototype之上,並用作創建實例的模板。它們(就像在任何其他語言中一樣)本質上是命令式的(imperative)。
如此一來,class可謂與FP的原則背道而馳,所以在大多數情況下,我們盡量也應該避免使用class。但不得不承認,有些時候,class也有最佳的使用時機與意義(例如昨天的例子)。
每當使用class時,問問自己是否尤其必要性,是否可以用declarative的方式表達,是否只用function就可以實現。
依照我老闆的話,九成的情況下,是可以的!
現在就讓我們一步步重構class以達成declarative的風格。
class
與實例以下這個classUserBuilder
主要會格式化使用者名稱formatting()
與打招呼greeting()
。
// 可能會被FP老闆討厭的寫法
// class
class UserBuilder {
constructor(userName) {
this.userName = userName;
}
formatting() {
this.userName = this.userName
.split(' ')
.map(s => s.charAt(0).toUpperCase() + s.substring(1))
.join(' ');
}
greeting() {
this.userName = `Welcome to Ryvn's home, ${this.userName}!`;
}
get changedName() {
return this.userName;
}
}
// main process
const user = new UserBuilder('mira austen');
user.formatting();
user.greeting();
const newUserName = user.changedName;
console.log(newUserName);
// Welcome to Ryvn's home, Mira Austen!
FP老闆的點評:
formatting()
與greeting()
的邏輯,因此使用該class的人可以不必在乎實現細節和過程中的每個步驟。也就是達成了封裝encapsulation的目的。new
關鍵字創造UserBuilder
的實例這個行為沒有為某種我們需要的目的或需求服務。class
與static
不牽扯instance最直覺的想法就是使用靜態方法static method。因此,或許可以改成下面形式:
// 可能沒這麼討厭的寫法
// class
class UserBuilder {
static formatting(userName) {
return userName
.split(' ')
.map(s => s.charAt(0).toUpperCase() + s.substring(1))
.join(' ');
}
static greeting(userName) {
return `Welcome to Ryvn's home, ${userName}!`;
}
}
// main process
const formattedUserName = UserBuilder.formatting('mira austen');
const greetingString = UserBuilder.greeting(formattedUserName);
console.log(greetingString);
// Welcome to Ryvn's home, Mira Austen!
FP老闆的點評:
function
與export
/import
為了完全去除class,我們可以使用function
與export
/import
達成相同目的。
首先,在userbuilder.js
中,我們可以這樣寫:
// 不太被討厭的寫法
export function formatting(userName) {
return userName
.split(' ')
.map(s => s.charAt(0).toUpperCase() + s.substring(1))
.join(' ');
}
export function greeting(userName) {
return `Welcome to Ryvn's home, ${userName}!`;
}
而在使用之處,則:
// 不太被討厭的寫法
import { formatting, greeting } from "./userbuilder";
const formattedUserName = formatting('mira austen');
const greetingString = greeting(formattedUserName);
console.log(greetingString);
// Welcome to Ryvn's home, Mira Austen!
現在,我們的程序完全是declarative的,這不僅讓我們code更容易測試,容易debug,並且是treeshakable(藉由例如Webpack或Rollup排除沒用到的utilities,以便獲得更小的bundle size)。更重要的是,可能會讓FP老闆開心。